import * as ol from "ol";
import { Geometry, Point } from "ol/geom";
import * as Layer from "ol/layer";
import { fromLonLat, toLonLat } from "ol/proj";
import * as Source from "ol/source";
import { Circle, Fill, Stroke } from "ol/style";
import * as keys from "./keys";
import { log } from "./log";
import { getMap } from "./instance";
import { _renderMarkerFeature } from "./marker";
import { ClusterMarkerOptions, RenderClusterLayerOptions } from "./types";
import { parseLngLat } from "./utils";
import { excludeProperties } from "@utils";

/**
 * @description: 渲染点聚合图层
 * @param {RenderMarkerLayerOptions} options 配置项
 * @returns {Layer.Vector<Source.Cluster<ol.Feature<Geometry>>>}
 */
export const renderClusterLayer = (
  options: RenderClusterLayerOptions,
): Layer.Vector<Source.Cluster<ol.Feature<Geometry>>> => {
  const mapInstance = getMap();
  if (!mapInstance) {
    throw new Error("地图实例未初始化");
  }
  const {
    type,
    pixel = 1,
    distance,
    minDistance,
    markers,
    renderClusterMarker,
    labelStyle: defaultLabelStyle,
    imageStyle: defaultImageStyle,
    stopClick: defaultStopClick,
    stopDBClick: defaultStopDBClick,
    visible = true,
    onClick,
    onDBClick,
    onMouseEnter,
    onMouseLeave,
    onClusterClick,
    onClusterDBClick,
    onClusterMouseEnter,
    onClusterMouseLeave,
  } = options;
  let layer = mapInstance.getAllLayers().find((item) => item.get(keys.LAYER_TYPE_KEY) === type) as
    | Layer.Vector<Source.Cluster<ol.Feature<Geometry>>>
    | undefined;
  if (!(layer instanceof Layer.Vector)) {
    layer = new Layer.Vector<Source.Cluster<ol.Feature<Geometry>>>({
      source: new Source.Cluster<ol.Feature<Geometry>>({
        distance,
        minDistance,
        createCluster: (point, features) => {
          if (features.length === 1) {
            // 渲染独立点位
            const [feature] = features;
            if (feature && feature.get(keys.OPTIONS_KEY)) {
              const {
                name,
                position,
                image,
                imageStyle,
                label,
                labelStyle,
                extData,
                stopClick,
                stopDBClick,
              } = feature.get(keys.OPTIONS_KEY) as ClusterMarkerOptions;

              return _renderMarkerFeature({
                type: "cluster-marker",
                layerType: type,
                name,
                position: parseLngLat(position),
                image,
                imageStyle: { ...imageStyle, ...defaultImageStyle },
                label,
                labelStyle: { ...labelStyle, ...defaultLabelStyle },
                extData,
                clusters: features.map((f) => f.get(keys.OPTIONS_KEY)),
                stopClick: stopClick ?? defaultStopClick,
                stopDBClick: stopDBClick ?? defaultStopDBClick,
                onClick,
                onDBClick,
                onMouseEnter,
                onMouseLeave,
              });
            }
          } else if (features.length > 1) {
            // 渲染聚合点位

            // 自定义渲染
            if (typeof renderClusterMarker === "function") {
              const {
                name,
                image,
                imageStyle,
                label,
                labelStyle,
                extData,
                stopClick,
                stopDBClick,
              } = renderClusterMarker.call(
                null,
                features.length,
                features.map((feature) => feature.get(keys.OPTIONS_KEY)),
              );

              return _renderMarkerFeature({
                type: "cluster-controller",
                layerType: type,
                name,
                position: parseLngLat(toLonLat(point.getCoordinates())),
                image,
                imageStyle: { ...imageStyle, ...defaultImageStyle },
                label,
                labelStyle: { ...labelStyle, ...defaultLabelStyle },
                extData,
                clusters: features.map((feature) => feature.get(keys.OPTIONS_KEY)),
                stopClick: stopClick ?? defaultStopClick,
                stopDBClick: stopDBClick ?? defaultStopDBClick,
                onClick: onClusterClick,
                onDBClick: onClusterDBClick,
                onMouseEnter: onClusterMouseEnter,
                onMouseLeave: onClusterMouseLeave,
              });
            }

            // 渲染聚合点默认样式
            return _renderMarkerFeature({
              type: "cluster-controller",
              layerType: type,
              position: parseLngLat(toLonLat(point.getCoordinates())),
              label: String(features.length),
              image: new Circle({
                radius: (pixel * (20 * String(features.length).length + 12)) / 2,
                stroke: new Stroke({
                  color: "#9fcdff",
                }),
                fill: new Fill({
                  color: "#003e72",
                }),
              }),
              labelStyle: {
                font: "2rem sans-serif",
                textAlign: "center",
                justify: "center",
                fill: new Fill({ color: "white" }),
              },
              clusters: features.map((feature) => feature.get(keys.OPTIONS_KEY)),
              onClick: onClusterClick,
              onDBClick: onClusterDBClick,
              onMouseEnter: onClusterMouseEnter,
              onMouseLeave: onClusterMouseLeave,
            });
          }
          return new ol.Feature();
        },
      }),
      ...excludeProperties(options, [
        "markers",
        "textStyle",
        "imageStyle",
        "stopClick",
        "stopDBClick",
        "onClick",
        "onDBClick",
        "onMouseEnter",
        "onMouseLeave",
        "onClusterClick",
        "onClusterDBClick",
        "onClusterMouseEnter",
        "onClusterMouseLeave",
      ]),
    });
    layer.set(keys.LAYER_TYPE_KEY, type);
    mapInstance.addLayer(layer);
  }

  log("Render Cluster Layer", options);

  // 设置数据源
  layer.getSource()?.setSource(
    new Source.Vector<ol.Feature<Geometry>>({
      features: (Array.isArray(markers) ? markers : [markers])
        .filter(({ position }) => Array.isArray(parseLngLat(position)))
        .map((marker) => {
          const feature = new ol.Feature({
            geometry: new Point(fromLonLat(parseLngLat(marker.position) as number[])),
          });
          feature.set(keys.OPTIONS_KEY, marker);

          return feature;
        }),
    }),
  );
  layer.setVisible(visible);

  return layer;
};

/**
 * @description: 显示或渲染点聚合图层
 * @param {RenderMarkerLayerOptions} options 配置项
 * @returns {Layer.Vector<Source.Cluster<ol.Feature<Geometry>>>}
 */
export const showOrRenderClusterLayer = (
  options: RenderClusterLayerOptions,
): Layer.Vector<Source.Cluster<ol.Feature<Geometry>>> => {
  const mapInstance = getMap();
  if (!mapInstance) {
    throw new Error("地图实例未初始化");
  }
  const { type, visible = true } = options;
  const layer = mapInstance
    .getAllLayers()
    .find((item) => item.get(keys.LAYER_TYPE_KEY) === type) as
    | Layer.Vector<Source.Cluster<ol.Feature<Geometry>>>
    | undefined;
  if (layer instanceof Layer.Vector) {
    log("Change Cluster Layer Visible", options);
    layer.setVisible(visible);
    return layer;
  }

  return renderClusterLayer(options);
};
